home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / The World of Computer Software.iso / xtndpath.zip / XTNDPATH.C < prev    next >
Text File  |  1993-01-12  |  63KB  |  1,490 lines

  1. #include <stdio.h>
  2. #include <dos.h>
  3. #include <string.h>
  4. #include <conio.h>
  5. #include <alloc.h>
  6.  
  7. /*
  8.  *  Utility:  XTNDPATH  (eXTeND MS-DOS environment PATH length -- FAST!)
  9.  *
  10.  *  Author:    G. David Douglas, Jr.  (douglas@usceast.cs.scarolina.edu)
  11.  *  Date:       1/12/TU/93
  12.  *  Compiler:  Turbo C 1.5 under MS-DOS 4.01, using Compact memory model
  13.  *
  14.  *
  15.  *  Usage:
  16.  *
  17.  *  XTNDPATH [-REV] [-PREFIX AlternatePrefixString]
  18.  *
  19.  *  or
  20.  *
  21.  *  XTNDPATH Anything-else           (for a help screen to be shown)
  22.  *
  23.  *  All items in brackets are optional and must be specified in the shown
  24.  *  order; don't use brackets when giving optional items.  For example,
  25.  *  use
  26.  *
  27.  *  XTNDPATH -REV
  28.  *
  29.  *  not
  30.  *
  31.  *  XTNDPATH [-REV]
  32.  *
  33.  *  XTNDPATH will extend the DOS PATH length by appending all environment
  34.  *  entries that begin with PATHPART (the default PrefixString).
  35.  *
  36.  *  If -REV is specified (MUST BE FIRST PARAMETER), XTNDPATH displays
  37.  *  information in reverse video (black characters on white background).
  38.  *
  39.  *  If -PREFIX AlternatePrefixString is specified, then the given
  40.  *  AlternatePrefixString is used instead of the default PATHPART prefix.
  41.  *
  42.  *  Possible ways of giving the XTNDPATH command:
  43.  *
  44.  *
  45.  *  XTNDPATH                          (to simply extend the DOS PATH length by
  46.  *                                     appending all environment entries that
  47.  *                                     begin with PATHPART )
  48.  *
  49.  *  XTNDPATH -REV                     (to extend the DOS PATH in the same way
  50.  *                                     as when XTNDPATH is used with no
  51.  *                                     parameters, but display information
  52.  *                                     output in black characters on white
  53.  *                                     background.)
  54.  *
  55.  *  XTNDPATH -PREFIX AlternatePrefixString (to extend the DOS PATH length
  56.  *                                          by appending all environment
  57.  *                                          entries that begin with
  58.  *                                          some AlternatePrefixString,
  59.  *                                          e.g., PATHPIECE)
  60.  *
  61.  *  XTNDPATH -REV -PREFIX AlternatePrefixString  (combines the last two
  62.  *                                                given cases)
  63.  *
  64.  *  XTNDPATH anything-else                 (To display help text directly)
  65.  *
  66.  *  For instance,  XTNDPATH HELP
  67.  *
  68.  *
  69.  *  This utility, when run, searches through ALL memory blocks allocated
  70.  *  by MS-DOS for any in use by COMMAND.COM, and, for each COMMAND.COM
  71.  *  environment block it finds, checks to see if (a) a PATH= entry exists
  72.  *  there, and (b) if any entries of the form PATHPART= or PATHPART1=,
  73.  *  PATHPART2=, PATHPARTqwerty=, etc. exist.
  74.  *
  75.  *  The default prefix for appended entries is PATHPART; an alternate
  76.  *  prefix can be used by giving the -PREFIX option followed by the
  77.  *  alternate prefix on the command line.
  78.  *
  79.  *  If both conditions (a) and (b) are satisfied, it edits the PATH=
  80.  *  statement, appending all PATHPARTn= commands to the end of the PATH=
  81.  *  statement, in the order IN WHICH THEY ARE FOUND, and then displays a
  82.  *  one-line message showing the location of the environment block modified
  83.  *  along with the original and new PATH= lengths.  In other words, if
  84.  *
  85.  *  PATHPART2=C:\TEMP;C:\TEXTDIR;
  86.  *  PATH=C:\;C:\DOS;
  87.  *  PATHPARTQWERTY=C:\QWERTY;
  88.  *
  89.  *  are found, then regardless of the fact that PATHPART2 was found
  90.  *  before PATHPARTqwerty, PATH= is modified to be
  91.  *
  92.  *  PATH=C:\;C:\DOS;C:\TEMP;C:\TEXTDIR;C:\QWERTY;
  93.  *
  94.  *
  95.  *  The same result may be accomplished with
  96.  *
  97.  *  PATHPIECE2=C:\TEMP;C:\TEXTDIR;
  98.  *  PATH=C:\;C:\DOS;
  99.  *  PATHPIECEQWERTY=C:\QWERTY;
  100.  *  XTNDPATH -PREFIX PATHPIECE
  101.  *
  102.  *
  103.  *  Note that each original path component, that is, the PATH= component
  104.  *  or each PATHPARTn= component, is considered a complete path in and
  105.  *  of itself.  However, if there are two path components to be joined, and
  106.  *  the first one does NOT end with a ';' (semicolon), one will be included
  107.  *  to keep the resulting extended path valid.
  108.  *
  109.  *  The utility does not consider some characters valid for inclusion in a
  110.  *  PATH statement; therefore, if any of these are found, they are ignored.
  111.  *  See the definition of INVALID_PATH_CHARACTERS, found immediately after
  112.  *  this header comment.
  113.  *
  114.  *  IMPORTANT:  It is possible, having run XTNDPATH, for the PATH= statement
  115.  *  to exceed 255 characters.  If, under these conditions, after XTNDPATH
  116.  *  is run, another PATH= statement is given on the command line, part of
  117.  *  the previously extended PATH= statement may be ignored by COMMAND.COM,
  118.  *  producing an extra, un-deletable, environment entry.  For this reason,
  119.  *  XTNDPATH checks for COMMAND.COM environment entries which contain no
  120.  *  '=' character, and deletes them.  This is safe because COMMAND.COM allows
  121.  *  no environment entry to be created without the use of an '=' character.
  122.  *  XTNDPATH will do this correction, WITH OR WITHOUT a PATH= entry and any
  123.  *  PATHPARTn= entries in all found COMMAND.COM environment blocks.
  124.  *
  125.  *  Also note:  after the utility is done editing the PATH= statement,
  126.  *  all PATHPARTn= statements are deleted, and are NOT present in the
  127.  *  final modified environment.  The PATH= entry retains its original
  128.  *  relative position in the modified environment's entries.
  129.  *
  130.  *  Final note:  for safety reasons, ONLY environment areas directly
  131.  *  used by some copy of COMMAND.COM might be modified; all others are
  132.  *  ignored.  This is because I cannot be sure that some utilities don't
  133.  *  directly modify their environment areas.
  134.  *
  135.  *  This utility was inspired in part by the BIGPATH utility, written by
  136.  *  Brian Moran, brian@mirror.TMC.COM  .  It has been tested successfully
  137.  *  in extending the PATH= environment entry to over 1200 characters, after
  138.  *  which the search for executables by COMMAND.COM is STILL successful!
  139.  *
  140.  *
  141.  *  THIS SOURCE CODE IS ORIGINALLY (C)OPYRIGHT 1993 DAVID DOUGLAS,
  142.  *  douglas@usceast.cs.scarolina.edu .  IT IS HEREBY PLACED IN THE PUBLIC
  143.  *  DOMAIN AS FAR AS FURTHER USE AND MODIFICATION IS CONCERNED.  (THIS NOTICE
  144.  *  MUST BE INCLUDED IN ANY MODIFIED VERSION OF THIS SOURCE CODE.)  USE THIS
  145.  *  SOURCE CODE AT YOUR OWN RISK; ALL STANDARD DISCLAIMERS APPLY.  IT WORKS
  146.  *  EXTREMELY WELL FOR ME; I HOPE YOU FIND IT AS USEFUL AS I HAVE.
  147.  */
  148.  
  149. #define NOT_FINAL_MCB_CODE   0x4D                 /* 'M' */
  150. #define FINAL_MCB_CODE       0x5A                 /* 'Z' */
  151. #define BYTES_PER_PARAGRAPH  16
  152.  
  153. #define MAX_MCB_NAME_LEN     8
  154.  
  155. #define INVALID_PATH_CHARACTERS "*+|=,?\"<> []/"
  156.  
  157. #define DEFAULT_PREFIX_STRING "PATHPART"          /* IMPORTANT!  DON'T USE   */
  158.                                                   /* ANY '=' CHARACTERS!!!   */
  159.  
  160. typedef struct {
  161.                 unsigned char mcb_entry_code;     /* Last, or not last MCB?  */
  162.                 unsigned int  mcb_psp_segment;    /* Segment of Program-     */
  163.                                                   /* Segment-Prefix with     */
  164.                                                   /* which this Memory       */
  165.                                                   /* Control Block is        */
  166.                                                   /* associated              */
  167.  
  168.                 unsigned int  mcb_size;           /* Size of Memory Control  */
  169.                                                   /* Block area in segments  */
  170.                                                   /* (16-byte paragraphs)    */
  171.  
  172.                 unsigned char
  173.                           unknown_mcb_info[3];    /* Three bytes in Memory   */
  174.                                                   /* Control Block regarding */
  175.                                                   /* whose purpose I have no */
  176.                                                   /* information.            */
  177.  
  178.                 unsigned char mcb_name
  179.                           [MAX_MCB_NAME_LEN];     /* Possible program name   */
  180.                                                   /* associated with Memory  */
  181.                                                   /* Control Block           */
  182.  
  183.                } mem_control_block;   /* Definition for Memory Control Block */
  184.  
  185.  
  186. /*****************************************************************************/
  187. /************************** XTNDPATH - MAIN PROGRAM **************************/
  188. /*****************************************************************************/
  189.  
  190.  
  191. main(argc, argv)
  192.  
  193. int  argc;
  194. char *argv[];
  195.  
  196.        {
  197.  
  198.         int               i;                                /* Loop variable */
  199.         struct REGPACK    cpu_registers;
  200.         unsigned int      *word_pointer;
  201.         unsigned char     *byte_pointer;
  202.         mem_control_block *mcb_pointer,
  203.                           *mcb_pointer_copy,
  204.                           *environment_mcb_pointer;
  205.  
  206.         unsigned char     *environment_block_pointer;
  207.  
  208.         unsigned char     *prefix_string,
  209.                           *prefix_string_copy;
  210.  
  211.         int               prefix_string_length;
  212.  
  213.  
  214.         unsigned char     *source_string_ptr,
  215.                           *destination_string_ptr;
  216.  
  217.         char program_name
  218.                      [MAX_MCB_NAME_LEN+1];   /* One more entry for NULL char */
  219.  
  220.         int  reverse_video_flag;             /* Display modification         */
  221.                                              /* information in black         */
  222.                                              /* characters on white          */
  223.                                              /* background, or not? (1 or 0) */
  224.  
  225.         int               nostop(void);      /* "Don't stop" routine         */
  226.                                              /* to be used with the builtin  */
  227.                                              /* ctrlbrk routine.             */
  228.  
  229.         void modify_environment(unsigned char *,
  230.                                 unsigned int,
  231.                                 unsigned char *,
  232.                                 int);               /* Program environment   */
  233.                                                     /* modification routine. */
  234.  
  235.         void usage(void);                    /* Routine to tell user how to  */
  236.                                              /* use XTNDPATH.                */
  237.  
  238.         void *emalloc(unsigned int);         /* Memory allocation and        */
  239.         void efree(void *);                  /* freeing routines.            */
  240.  
  241.         /*
  242.          *  First, set things up so that <Control>C or <Control><Break>
  243.          *  won't interrupt anything.
  244.          */
  245.  
  246.         ctrlbrk(nostop);
  247.  
  248.         /*
  249.          *  Let the  reverse_video_flag  variable default to a value of 0
  250.          *  (use current text attributes for displaying environment
  251.          *  modification information).  This value will be changed if
  252.          *  -REV is the first parameter on the command line; in that
  253.          *  case, environment modification information will displayed using
  254.          *  black characters on a white background.
  255.          */
  256.  
  257.         reverse_video_flag = 0;
  258.  
  259.         /*
  260.          *  Now see if the user gave any command line parameters.  If there
  261.          *  are any, and the first parameter is not -REV or -PREFIX, then
  262.          *  assume the user wants help and call the  usage  procedure to
  263.          *  display a description of what this utility does, and exit.
  264.          *
  265.          *  Otherwise:
  266.          *
  267.          *  See if the first given parameter (uppercased) is -REV.  If so,
  268.          *  then set the  reverse_video_flag  to 1 to indicate to the
  269.          *  modify_environment procedure that modification information output
  270.          *  is to be displayed in black characters on white background.  If
  271.          *  the first given parameter is -REV, delete it from the command
  272.          *  parameters.
  273.          *
  274.          *  Now see if the current first parameter (uppercased) is -PREFIX.
  275.          *  If so, then use the next parameter (if given; if not, use PATHPART)
  276.          *  in place of PATHPART as the prefix for appended entries, UNLESS
  277.          *  the new prefix string is PATH; in this case, default to PATHPART.
  278.          */
  279.  
  280.         if(argc > 1)
  281.           {
  282.            strupr(argv[1]);                    /* Convert first parameter    */
  283.                                                /* to uppercase.              */
  284.  
  285.            if((strcmp(argv[1], "-REV") != 0) &&
  286.               (strcmp(argv[1], "-PREFIX") != 0)) /* Display help information */
  287.              {
  288.               usage();
  289.               return(0);
  290.              }
  291.            else
  292.              {
  293.               /*
  294.                *  If first parameter is -REV, set reverse_video_flag to
  295.                *  1, and delete the first parameter from the parameter list.
  296.                */
  297.  
  298.               if(strcmp(argv[1], "-REV") == 0)
  299.                 {
  300.                  reverse_video_flag = 1;
  301.  
  302.                  efree((void *)argv[1]);
  303.  
  304.                  for(i = 1; i < argc - 1; i++)
  305.                    argv[i] = argv[i+1];
  306.  
  307.                  argc--;
  308.  
  309.                 }
  310.  
  311.               /*
  312.                *  Check current first parameter.  If it is -PREFIX, process
  313.                *  it and the next (possible) parameter appropriately.
  314.                *  Otherwise, since current first parameter is not -REV or
  315.                *  -PREFIX, and the user help case was tested for earlier,
  316.                *  use the default prefix string.
  317.                */
  318.  
  319.               /*
  320.                *  In case any given "-REV" parameter was deleted earlier,
  321.                *  uppercase the current first parameter (maybe again).
  322.                */
  323.  
  324.               strupr(argv[1]);
  325.  
  326.               if(strcmp(argv[1],
  327.                         "-PREFIX") == 0)       /* Check out alternate prefix */
  328.                 {
  329.                  if(argc > 2)                  /* Use parameter 2 as prefix, */
  330.                                                /* and assume (for now) user  */
  331.                                                /* knows what he's doing in   */
  332.                                                /* choosing characters that   */
  333.                                                /* make up an alternate       */
  334.                                                /* prefix.                    */
  335.                    {
  336.                     /*
  337.                      *  Use the alternate prefix, UNLESS it is PATH or PATH=,
  338.                      *  which is not safe, since we would be appending all
  339.                      *  entries with PATH to the PATH= entry, which doesn't
  340.                      *  make sense.  In that case, use the default prefix
  341.                      *  instead.
  342.                      */
  343.  
  344.                     strupr(argv[2]);   /* Convert prefix string to uppercase */
  345.  
  346.                     if((strcmp(argv[2], "PATH") != 0) &&
  347.                        (strcmp(argv[2], "PATH=") != 0))
  348.                       {
  349.                        prefix_string = (unsigned char *)emalloc((unsigned int)
  350.                                                         (strlen(argv[2]) + 1));
  351.                        strcpy(prefix_string, argv[2]);
  352.                       }
  353.                     else
  354.                       {
  355.                        prefix_string = (unsigned char *)emalloc((unsigned int)
  356.                                           (strlen(DEFAULT_PREFIX_STRING) + 1));
  357.                        strcpy(prefix_string, DEFAULT_PREFIX_STRING);
  358.  
  359.                        strupr(prefix_string);       /* Convert to uppercase, */
  360.                                                     /* just in case          */
  361.                       }
  362.                    }
  363.                  else
  364.                    {         /* No 2nd parameter given -- use default prefix */
  365.                     prefix_string = (unsigned char *)emalloc((unsigned int)
  366.                                           (strlen(DEFAULT_PREFIX_STRING) + 1));
  367.  
  368.                     strcpy(prefix_string, DEFAULT_PREFIX_STRING);
  369.                     strupr(prefix_string);          /* Convert to uppercase, */
  370.                    }                                /* just in case          */
  371.  
  372.                 }
  373.               else
  374.                 {                               /* Use default prefix string */
  375.                  prefix_string = (unsigned char *)emalloc((unsigned int)
  376.                                           (strlen(DEFAULT_PREFIX_STRING) + 1));
  377.  
  378.                  strcpy(prefix_string, DEFAULT_PREFIX_STRING);
  379.                  strupr(prefix_string);             /* Convert to uppercase, */
  380.                                                     /* just in case          */
  381.                 }
  382.  
  383.              }
  384.  
  385.           }
  386.         else
  387.           {
  388.            /*
  389.             *  Otherwise, no arguments were given.  Use the default prefix
  390.             *  string.
  391.             */
  392.  
  393.            prefix_string = (unsigned char *)emalloc((unsigned int)
  394.                                           (strlen(DEFAULT_PREFIX_STRING) + 1));
  395.  
  396.            strcpy(prefix_string, DEFAULT_PREFIX_STRING);
  397.            strupr(prefix_string);      /* Convert to uppercase, just in case */
  398.           }
  399.  
  400.  
  401.         /*
  402.          *  Final safety check -- now that the default prefix string has
  403.          *  been assigned a "value", see if any '=' characters exist in
  404.          *  the prefix string.  If one or more '=' characters are found,
  405.          *  delete them.  If, in doing this deletion, the prefix string
  406.          *  becomes an empty string (originally contained all '=' characters),
  407.          *  use the default prefix string instead.
  408.          *
  409.          *  This final safety check is done because COMMAND.COM uses the '='
  410.          *  character as a delimiter in environment entries, and doesn't even
  411.          *  allow more than one '=' character to be used in a 'SET' command
  412.          *  (which of the two or more '=' characters would be the delimiter?).
  413.          */
  414.  
  415.         if(strchr(prefix_string, (int)'=') != NULL)
  416.           {
  417.            prefix_string_copy = (unsigned char *)
  418.                             emalloc((unsigned int)(strlen(prefix_string) + 1));
  419.  
  420.  
  421.            source_string_ptr      = prefix_string;
  422.            destination_string_ptr = prefix_string_copy;
  423.  
  424.            while(*source_string_ptr != (unsigned char)NULL)
  425.              {
  426.               if(*source_string_ptr != '=')
  427.                 {
  428.                  *destination_string_ptr = *source_string_ptr;
  429.                  source_string_ptr++;
  430.                  destination_string_ptr++;
  431.                 }
  432.               else source_string_ptr++;
  433.              }
  434.  
  435.            *destination_string_ptr = (unsigned char)NULL;
  436.  
  437.            if(strlen(prefix_string_copy) != 0)  /* Prefix string now valid - */
  438.                                                 /* copy it back to original  */
  439.              {
  440.               strcpy(prefix_string, prefix_string_copy);
  441.               efree((void *)prefix_string_copy);
  442.              }
  443.            else
  444.              {        /* Prefix string was all '=' characters -- use default */
  445.  
  446.               efree((void *)prefix_string_copy);
  447.               efree((void *)prefix_string);
  448.  
  449.               prefix_string = (unsigned char *)emalloc((unsigned int)
  450.                                           (strlen(DEFAULT_PREFIX_STRING) + 1));
  451.  
  452.               strcpy(prefix_string, DEFAULT_PREFIX_STRING);
  453.               strupr(prefix_string);   /* Convert to uppercase, just in case */
  454.              }
  455.  
  456.           }
  457.  
  458.         /*
  459.          *  Prefix string setup is now done.
  460.          *
  461.          *  Issue a PC-DOS service number 52h call to retrieve the
  462.          *  segment and offset of the last location "officially"
  463.          *  occupied by DOS.  Segment will be contained in the ES
  464.          *  pseudo-register, offset in the BX pseudo-register.  Subtract
  465.          *  2 from BX to get offset of the 2 bytes containing segment number
  466.          *  of the first Memory Control Block (MCB).
  467.          */
  468.  
  469.         cpu_registers.r_ax = 0x5200;  /* AH register <- 0x52,          */
  470.                                       /* AL register <- 0x00           */
  471.  
  472.         intr(0x21, &cpu_registers);
  473.  
  474.         cpu_registers.r_bx -= 2;
  475.  
  476.         /*
  477.          *  Load segment and offset into pointer variable -- r_es and
  478.          *  r_bx pseudo_registers (as well as actual registers ES and
  479.          *  BX) are 16 bits in width, so shift ES component left 16
  480.          *  bits before including the offset contained in BX.
  481.          */
  482.  
  483.         word_pointer = (unsigned int *)(((long)cpu_registers.r_es << 16) |
  484.                                          (long)cpu_registers.r_bx);
  485.  
  486.         /*
  487.          *  Now, retrieve the segment value pointed to by word_pointer,
  488.          *  and use it along with an offset of 0 as the starting Memory
  489.          *  Control Block address.  Use a character pointer for
  490.          *  initially checking Memory Control Blocks, since only the
  491.          *  first byte in the 16-byte Memory Control Block header
  492.          *  determines whether this is one of the starting blocks
  493.          *  (value 4Dh ('M')) or the last block (value 5Ah ('Z')).
  494.          */
  495.  
  496.         word_pointer = (unsigned int *)(((long)*word_pointer << 16) | 0x0000);
  497.  
  498.         byte_pointer = (unsigned char *)word_pointer;
  499.  
  500.         do
  501.           {
  502.            /*
  503.             *  Get a Memory-Control-Block pointer pointing to this
  504.             *  location, for easier referencing of its contents.
  505.             */
  506.  
  507.            mcb_pointer = (mem_control_block *)byte_pointer;
  508.  
  509.            /*
  510.             *  Apparently, under MS-DOS, only actual program code immediately
  511.             *  follows (in memory) the Memory Control Block header containing
  512.             *  that program code's starting segment.  Therefore, only
  513.             *  check memory control blocks for which this applies.
  514.             *
  515.             *  For any such found Memory Control Block, if the name field
  516.             *  contains "COMMAND" followed by a null (ASCII 0) character,
  517.             *  look at offset 2Ch for the segment of that code's environment
  518.             *  memory block.  Then check in the Memory Control Block of the
  519.             *  environment memory block.  If the entry indicating which
  520.             *  Program Segment Prefix it is associated with matches the
  521.             *  starting code segment of the earlier found COMMAND.COM
  522.             *  memory area, then we have a valid environment area to check
  523.             *  and possibly modify.
  524.             */
  525.  
  526.            if((mcb_pointer->mcb_psp_segment != 0) &&   /* NOT FREE in MS-DOS */
  527.               (((unsigned int)((long)mcb_pointer >> 16) + 1) ==
  528.                                                  mcb_pointer->mcb_psp_segment))
  529.              {
  530.  
  531.               for(i = 0; i < MAX_MCB_NAME_LEN; i++)
  532.                 {
  533.                  program_name[i] = mcb_pointer->mcb_name[i];
  534.                  if(program_name[i] == (unsigned char) 0)
  535.                    break;
  536.                 }
  537.  
  538.               program_name[MAX_MCB_NAME_LEN] = (char)0;
  539.  
  540.               if(strcmp(program_name, "COMMAND") == 0)
  541.                 {
  542.                  /*
  543.                   *  Check on the environment memory block for this program.
  544.                   *  Skip down 16 bytes to the actually memory block this
  545.                   *  Memory Control Block references, then look in location
  546.                   *  (more specifically, at offset) 2Ch for the segment of
  547.                   *  the corresponding memory control block.  If this value is
  548.                   *  zero, then this program doesn't use an environment
  549.                   *  memory block, so don't worry about it.  Otherwise, check
  550.                   *  the Memory Control Block corresponding to this possible
  551.                   *  environment memory block.  If the Program Segment Prefix
  552.                   *  field in the MCB, i.e., the PSP with which this memory
  553.                   *  block is associated, matches the PSP of the current
  554.                   *  COMMAND memory block being checked, then this IS a valid
  555.                   *  environment memory block, and can safely be modified.
  556.                   */
  557.  
  558.                  mcb_pointer_copy =
  559.                    (mem_control_block *)
  560.                               ((((unsigned long)mcb_pointer >> 16) + 1) << 16);
  561.  
  562.                  word_pointer = (unsigned int *)
  563.                    (((unsigned long)mcb_pointer_copy) | (unsigned long)0x002C);
  564.  
  565.                  environment_mcb_pointer = (mem_control_block *)
  566.                          ((((unsigned long)*word_pointer - 1) << 16) | 0x0000);
  567.  
  568.                  if((*word_pointer != 0) &&
  569.                     (mcb_pointer->mcb_psp_segment ==
  570.                                      environment_mcb_pointer->mcb_psp_segment))
  571.                    {
  572.  
  573.                     /*
  574.                      *  Get "byte" pointer to start of environment memory
  575.                      *  block, then call environment modification routine
  576.                      *  with this pointer, with the memory block size
  577.                      *  contained in the environment block Memory Control
  578.                      *  Block, with the environment entry prefix string
  579.                      *  for environment entries to be appended to the
  580.                      *  environment PATH entry, and with the reverse_video_flag
  581.                      *  variable.  The reverse_video_flag variable's value
  582.                      *  indicates (value 0) modification information output is
  583.                      *  to be done using current text attributes, or (value
  584.                      *  non-zero) modification information output is to be done
  585.                      *  using black characters on white background.
  586.                      */
  587.  
  588.                     environment_block_pointer =
  589.               (unsigned char *)(((unsigned long)*word_pointer << 16) | 0x0000);
  590.  
  591.                     modify_environment(environment_block_pointer,
  592.                        environment_mcb_pointer->mcb_size * BYTES_PER_PARAGRAPH,
  593.                        prefix_string,
  594.                        reverse_video_flag);
  595.                    }
  596.  
  597.                 }
  598.  
  599.              }
  600.  
  601.            /*
  602.             *  Now, if byte_pointer doesn't already point to the last Memory
  603.             *  Control Block, advance to the next Memory Control Block -- take
  604.             *  the segment portion of the MCB pointer, add in the number of
  605.             *  segments that the Memory Control Block occupies, then
  606.             *  add 1 more to advance to the beginning of the next
  607.             *  Memory Control Block.
  608.             */
  609.  
  610.            if(*byte_pointer != (unsigned char)FINAL_MCB_CODE)
  611.              byte_pointer = (unsigned char *)
  612.               ((((long)byte_pointer >> 16) + mcb_pointer->mcb_size + 1) << 16);
  613.  
  614.           }
  615.         while(*byte_pointer != (unsigned char)FINAL_MCB_CODE);
  616.  
  617.         /*
  618.          *  All memory blocks have been checked.  Now free the memory
  619.          *  occupied by the prefix-string, and exit the utility.
  620.          */
  621.  
  622.         efree((void *)prefix_string);
  623.  
  624.         return(0);
  625.        }                                     /* End of XTNDPATH main program */
  626.  
  627. /*****************************************************************************/
  628.  
  629.  
  630. /*
  631.  *  Function nostop
  632.  *
  633.  *  This function is used along with the builtin ctrlbrk routine.  This
  634.  *  function, by returning a non-zero value when called by the internal
  635.  *  code of ctrlbrk, indicates that <Ctrl>C or <Ctrl><Break> is NOT to
  636.  *  interrupt the program, but to allow execution to continue.
  637.  */
  638.  
  639. int nostop(void)
  640.    {
  641.     return(1);
  642.    }                                               /* End of function nostop */
  643.  
  644. /*****************************************************************************/
  645.  
  646.  
  647. /*
  648.  *  Procedure modify_environment
  649.  *
  650.  *  This procedure, given
  651.  *
  652.  *  (1) a character pointer to the start of a program environment area,
  653.  *
  654.  *  (2) the size of the environment in bytes,
  655.  *
  656.  *  (3) the prefix string for entries to be appended to the PATH= entry
  657.  *      (hereafter referred to simply as PATHPARTn), and
  658.  *
  659.  *  (4) a reverse-video-flag:  0 value indicates that modification information
  660.  *      output is to be done with current text attributes, non-zero value
  661.  *      indicates that modification information output is to be done using
  662.  *      black characters on white background,
  663.  *
  664.  *  reads all environment strings into separate dynamically allocated memory
  665.  *  areas, then appends all found PATHPARTn entries to the PATH entry.  All
  666.  *  PATHPARTn entries are then, as far as the final environment is concerned,
  667.  *  deleted.  The modified environment, with the changed PATH statement, and
  668.  *  all other appropriate environment entries, is written back out to the
  669.  *  original environment area, with the PATH entry in its original relative
  670.  *  position.  After the environment has been modified, the procedure displays
  671.  *  a one-line message, giving the memory location of the modified environment
  672.  *  memory block along with the original and new PATH lengths.
  673.  *
  674.  *  Note that individual environment entries are terminated by an ASCII 0
  675.  *  character (End-of-string);  the end of the environment area is signaled
  676.  *  by one ASCII 0 character, which terminates the final environment entry,
  677.  *  followed by one more ASCII 0 character.  If the environment is empty,
  678.  *  then the environment area has an ASCII 0 character at the very beginning.
  679.  *
  680.  *  IMPORTANT:  This procedure also checks for invalid environment entries
  681.  *              in the referenced memory block.  By invalid, I mean that an
  682.  *              entry does not have an '=' character in it, which COMMAND.COM
  683.  *              requires when an environment entry is created.  Any such
  684.  *              entries are deleted, and the procedure notifies the user.
  685.  *              This environment "repair" is done whether or not a PATH=
  686.  *              entry and one or more PATHPARTn= entries are found in the
  687.  *              environment.
  688.  */
  689.  
  690. void modify_environment(environment_pointer,
  691.                         environment_size,
  692.                         prefix_string,
  693.                         reverse_video_flag)
  694.  
  695. unsigned char *environment_pointer;
  696. unsigned int  environment_size;
  697. unsigned char *prefix_string;
  698. int           reverse_video_flag;
  699.  
  700.  
  701.         {
  702.          int      i, j;                              /* Loop variables       */
  703.          unsigned char *saved_environment_pointer;
  704.          unsigned char *old_environment_pointer;
  705.          unsigned int  old_environment_byte_count;
  706.          unsigned int  environment_byte_count;
  707.  
  708.          unsigned char **environment_strings;        /* Array of environment */
  709.                                                      /* strings, dynamically */
  710.                                                      /* allocated.           */
  711.  
  712.          unsigned char *environment_string;          /* Environment string,  */
  713.                                                      /* dynamically          */
  714.                                                      /* allocated.           */
  715.  
  716.          unsigned int environment_string_count;
  717.  
  718.          unsigned char *string_ptr;
  719.  
  720.          int path_string_index;                      /* Array index of PATH= */
  721.                                                      /* entry in             */
  722.                                                      /* environment_strings  */
  723.  
  724.          int *environment_string_indexes;            /* Array of indices to  */
  725.                                                      /* be used when doing   */
  726.                                                      /* final environment    */
  727.                                                      /* modification; all    */
  728.                                                      /* environment entries  */
  729.                                                      /* are accounted for,   */
  730.                                                      /* except for any       */
  731.                                                      /* PATHPARTn= entries.  */
  732.  
  733.          int environment_string_index_max;
  734.  
  735.          int *other_environment_indexes;             /* Indices of           */
  736.                                                      /* environment entries  */
  737.                                                      /* which contain        */
  738.                                                      /* PATHPARTn= entries.  */
  739.  
  740.          int original_path_length;
  741.  
  742.          int other_environment_index_max;
  743.  
  744.          int invalid_environment_entry_count;
  745.  
  746.          unsigned char *new_path_string;
  747.          int           new_path_length;
  748.          unsigned char *new_path_ptr;                /* Pointer to current   */
  749.                                                      /* position in new      */
  750.                                                      /* PATH= string         */
  751.  
  752.          unsigned char *path_entry_ptr;              /* Pointer to current   */
  753.                                                      /* position in          */
  754.                                                      /* environment entry    */
  755.                                                      /* being included in    */
  756.                                                      /* new PATH= string     */
  757.  
  758.          unsigned char last_character,               /* Last character put   */
  759.                        current_character;            /* into the (updated)   */
  760.                                                      /* PATH= environment    */
  761.                                                      /* entry, and current   */
  762.                                                      /* character being      */
  763.                                                      /* considered for       */
  764.                                                      /* inclusion in the     */
  765.                                                      /* (updated) PATH=      */
  766.                                                      /* environment entry.   */
  767.  
  768.          int prefix_string_length;
  769.  
  770.          void *emalloc(unsigned);                    /* Memory allocation,   */
  771.          void *erealloc(void *, unsigned);           /* re-allocation, and   */
  772.          void efree(void *);                         /* freeing routines.    */
  773.  
  774.  
  775.  
  776.          /*
  777.           *  Initialize all count and applicable pointer variables.
  778.           */
  779.  
  780.          environment_byte_count   = 0;
  781.          environment_string_count = 0;
  782.  
  783.  
  784.          prefix_string_length = strlen(prefix_string);
  785.  
  786.          /*
  787.           *  These variables are initialized to make it easier to decide
  788.           *  later whether or not they were ever given a value from emalloc
  789.           *  or erealloc.
  790.           */
  791.  
  792.          environment_strings        = (unsigned char **)NULL;
  793.          environment_string_indexes = (int *)NULL;
  794.          other_environment_indexes  = (int *)NULL;
  795.  
  796.          /*
  797.           *  Save the environment pointer value so it can be used again
  798.           *  when the possibly modified environment is written out
  799.           *  to its original memory area.
  800.           */
  801.  
  802.          saved_environment_pointer  = environment_pointer;
  803.  
  804.          /*
  805.           *  Try to process the environment area, but only if it
  806.           *  actually has environment entries.
  807.           */
  808.  
  809.          if(*environment_pointer != (unsigned char)0)
  810.            {
  811.             /*
  812.              *  Process environment entries until an ASCII 0 byte is
  813.              *  found following the last processed environment string,
  814.              *  or until all bytes in the environment memory block
  815.              *  have been processed.
  816.              */
  817.  
  818.             while((*environment_pointer != (unsigned char)0) &&
  819.                   (environment_byte_count < environment_size))
  820.               {
  821.                /*
  822.                 *  Count the number of bytes in the next environment
  823.                 *  string, then allocate enough memory for that many
  824.                 *  bytes for a string in a separate area, and copy the
  825.                 *  environment entry to that separate area.
  826.                 */
  827.  
  828.                old_environment_byte_count = environment_byte_count;
  829.                old_environment_pointer    = environment_pointer;
  830.  
  831.                while((*environment_pointer != (unsigned char)0) &&
  832.                      (environment_byte_count < environment_size))
  833.                  {
  834.                   environment_pointer++;
  835.                   environment_byte_count++;
  836.                  }
  837.  
  838.                /*
  839.                 *  Add 1 to environment_byte_count to account for last
  840.                 *  ASCII 0 character, if that's what environment_pointer
  841.                 *  currently points to.
  842.                 */
  843.  
  844.                if(*environment_pointer == (unsigned char)0)
  845.                  environment_byte_count++;
  846.  
  847.  
  848.                environment_string_count++;
  849.  
  850.                environment_strings = (unsigned char **)
  851.                                   erealloc((unsigned int *)environment_strings,
  852.                          (environment_string_count) * sizeof(unsigned char *));
  853.  
  854.                environment_string = (unsigned char *)emalloc((unsigned int)
  855.                       ((environment_byte_count - old_environment_byte_count) *
  856.                                                        sizeof(unsigned char)));
  857.  
  858.                environment_strings[environment_string_count-1] =
  859.                                                             environment_string;
  860.  
  861.  
  862.                string_ptr = environment_string;
  863.  
  864.                /*
  865.                 *  Copy the current environment entry to its corresponding
  866.                 *  work area, and terminate it with an ASCII 0 character.
  867.                 */
  868.  
  869.                while(old_environment_pointer != environment_pointer)
  870.                  {
  871.                   *string_ptr = *old_environment_pointer;
  872.                   string_ptr++;
  873.                   old_environment_pointer++;
  874.                  }
  875.  
  876.                *string_ptr = (unsigned char)0;
  877.  
  878.                environment_pointer++;
  879.  
  880.               }
  881.  
  882.             /*
  883.              *  Allocate enough memory for index arrays.  Then, search
  884.              *  through environment strings for one beginning with 'PATH='.
  885.              *  If an environment entry is found which is not of the form
  886.              *  PATH= or PATHPARTn=, then simply add its index to the
  887.              *  environment index list, UNLESS IT CONTAINS NO '=' CHARACTER;
  888.              *  in this case, IGNORE IT.  Otherwise, if the string is of the
  889.              *  form PATH=, add its index to the environment index list,
  890.              *  and record its index in the  path_string_index  variable.
  891.              *  On the other hand, if it is of the form PATHPARTn=, add its
  892.              *  index to the other environment index list.
  893.              *
  894.              *  If a PATH= entry was found, then append all PATHPARTn= entries
  895.              *  to it; otherwise, don't even bother to try modifying the
  896.              *  environment.  The exception to this rule is if any invalid
  897.              *  (contain no '=' character) entries were found; in this case,
  898.              *  the modified environment WILL be written back to its original
  899.              *  area, with the invalid entries deleted.
  900.              */
  901.  
  902.             environment_string_indexes = (int *)emalloc((unsigned int)
  903.                                      (environment_string_count * sizeof(int)));
  904.  
  905.             other_environment_indexes = (int *)emalloc((unsigned int)
  906.                                      (environment_string_count * sizeof(int)));
  907.  
  908.  
  909.             environment_string_index_max = -1;
  910.             other_environment_index_max  = -1;
  911.  
  912.             path_string_index = -1;
  913.  
  914.             for(i = 0; i < environment_string_count; i++)
  915.               {
  916.                if(strncmp("PATH=", environment_strings[i], 5) == 0)
  917.                  {
  918.                   path_string_index = i;
  919.                   environment_string_index_max++;
  920.                   environment_string_indexes[environment_string_index_max] = i;
  921.                  }
  922.                else
  923.                  {
  924.                   if(strncmp(prefix_string,
  925.                              environment_strings[i],
  926.                              prefix_string_length) == 0)
  927.                     {
  928.                      /*
  929.                       *  Check for invalid environment entry, even in this
  930.                       *  case.
  931.                       */
  932.  
  933.                      string_ptr = environment_strings[i];
  934.                      while((*string_ptr != (unsigned char)0) &&
  935.                            (*string_ptr != '='))
  936.                        string_ptr++;
  937.  
  938.                      if(*string_ptr == '=')      /* Valid entry -- record it */
  939.                        {
  940.                         other_environment_index_max++;
  941.                         other_environment_indexes[other_environment_index_max]
  942.                                                                            = i;
  943.                        }
  944.                     }
  945.                   else
  946.                     {
  947.                      /*
  948.                       *  Check for invalid environment entry.  If the entry
  949.                       *  is valid, however, record it in the main environment
  950.                       *  index list.
  951.                       */
  952.  
  953.                      string_ptr = environment_strings[i];
  954.                      while((*string_ptr != (unsigned char)0) &&
  955.                            (*string_ptr != '='))
  956.                        string_ptr++;
  957.  
  958.                      if(*string_ptr == '=')      /* Valid entry -- record it */
  959.                        {
  960.                         environment_string_index_max++;
  961.                         environment_string_indexes
  962.                                             [environment_string_index_max] = i;
  963.                        }
  964.                     }
  965.                  }
  966.               }
  967.  
  968.             /*
  969.              *  If a PATH= entry was found, and one or more PATHPARTn= entries
  970.              *  were found, then append all PATHPARTn= entries, in the order
  971.              *  in which they were found, to the PATH= entry, in another
  972.              *  memory work area.  Then, delete the copy of the original
  973.              *  PATH= string, and replace it with the new one.
  974.              */
  975.  
  976.             if((path_string_index != -1) &&
  977.                (other_environment_index_max != -1))
  978.               {
  979.                /*
  980.                 *  First, allocate enough memory for a string that can hold
  981.                 *  the entire final 'PATH=' environment entry, including
  982.                 *  any invalid characters it might have (which will be
  983.                 *  deleted later).
  984.                 */
  985.  
  986.                new_path_length = strlen(environment_strings
  987.                                                           [path_string_index]);
  988.  
  989.                for(i = 0; i <= other_environment_index_max; i++)
  990.                  new_path_length +=
  991.                      strlen(environment_strings[other_environment_indexes[i]]);
  992.  
  993.                new_path_string = (unsigned char *)
  994.         emalloc((unsigned int)((new_path_length + 1) * sizeof(unsigned char)));
  995.  
  996.                new_path_ptr = new_path_string;
  997.  
  998.                /*
  999.                 *  Now copy the PATH= and all PATHPART environment entries to
  1000.                 *  the new string.  Copy 'PATH=' directly, then ignore invalid
  1001.                 *  characters.  For all PATHPART entries, ignore everything
  1002.                 *  until after the first '=' character, then ignore invalid
  1003.                 *  characters.  In all cases, try to keep the directory-name-
  1004.                 *  separators '\' and ';' path entry separators, as well as
  1005.                 *  the ':' drive name indicator correctly used.  (For instance,
  1006.                 *  you shouldn't have '\\' or ';;' or '::' in a PATH=
  1007.                 *  statement; this is the extent of this kind of error
  1008.                 *  correction.)  Note that this parsing isn't the smartest
  1009.                 *  thing around, but it should hold down most errors.  Also
  1010.                 *  note that this parsing automatically places a ';' between
  1011.                 *  each included PATH= component (previously in PATH= or
  1012.                 *  PATHPARTn=) unless a ';' is already present.
  1013.                 */
  1014.  
  1015.                last_character    = (unsigned char)0;
  1016.                current_character = (unsigned char)0;
  1017.  
  1018.                path_entry_ptr = environment_strings[path_string_index];
  1019.  
  1020.                /*
  1021.                 *  Copy the PATH= piece of the original PATH= entry, and
  1022.                 *  move just beyond it.
  1023.                 */
  1024.  
  1025.                for(j = 0; j < 5; j++)
  1026.                  {
  1027.                   *new_path_ptr = *path_entry_ptr;
  1028.                   new_path_ptr++;
  1029.                   path_entry_ptr++;
  1030.                  }
  1031.  
  1032.                /*
  1033.                 *  Copy the rest of the original PATH= entry, ignoring
  1034.                 *  invalid characters and character combinations.
  1035.                 */
  1036.  
  1037.                while(*path_entry_ptr != (unsigned char)0)
  1038.                  {
  1039.                   /*
  1040.                    *  Check out the next character available for inclusion
  1041.                    *  in the PATH= statement.  If it is invalid, ignore it,
  1042.                    *  otherwise check it further.
  1043.                    */
  1044.  
  1045.                   if(strchr(INVALID_PATH_CHARACTERS,
  1046.                                                  (int)*path_entry_ptr) == NULL)
  1047.                     {
  1048.                      /*
  1049.                       *  Character was not invalid, so now eliminate
  1050.                       *  anything like '\\' or ';;' or '::'.
  1051.                       */
  1052.  
  1053.                      current_character = *path_entry_ptr;
  1054.                      if(!((current_character == last_character) &&
  1055.                            ((current_character == '\\') ||
  1056.                             (current_character == ';') ||
  1057.                             (current_character == ':'))
  1058.                          )
  1059.                        )
  1060.                        {
  1061.                         *new_path_ptr = current_character;
  1062.                         last_character = current_character;
  1063.                         new_path_ptr++;
  1064.                        }
  1065.                     }
  1066.  
  1067.                   /*
  1068.                    *  Regardless of whether or not the character just
  1069.                    *  then considered was valid, move the path entry
  1070.                    *  pointer to the next character to be considered.
  1071.                    */
  1072.  
  1073.                   path_entry_ptr++;
  1074.  
  1075.                  }
  1076.  
  1077.                /*
  1078.                 *  Now append all PATHPARTn= environment entries, ignoring
  1079.                 *  invalid characters and character combinations.
  1080.                 */
  1081.  
  1082.                for(i = 0; i <= other_environment_index_max; i++)
  1083.                  {
  1084.                   path_entry_ptr =
  1085.                              environment_strings[other_environment_indexes[i]];
  1086.  
  1087.                   /*
  1088.                    *  Skip to just beyond the first '=' in the string.
  1089.                    *  Only include this entry if an '=' is found.  (This
  1090.                    *  check really isn't necessary, since it was determined
  1091.                    *  earlier that any entry with an index in the
  1092.                    *  other_environment_indexes list DOES have an '='
  1093.                    *  character in it.)
  1094.                    */
  1095.  
  1096.                   while((*path_entry_ptr != (unsigned char)0) &&
  1097.                         (*path_entry_ptr != '='))
  1098.                     path_entry_ptr++;
  1099.  
  1100.                   if(*path_entry_ptr != '=')
  1101.                                    /* Skip this environment entry completely */
  1102.                     continue;         /* Continue at the end of the for loop */
  1103.                   else path_entry_ptr++;
  1104.  
  1105.                   /*
  1106.                    *  If the last character included in the modified PATH=
  1107.                    *  statement was not a ';', include a ';' to separate the
  1108.                    *  included components correctly.
  1109.                    */
  1110.  
  1111.                   if(last_character != ';')
  1112.                     {
  1113.                      *new_path_ptr = ';';
  1114.                      new_path_ptr++;
  1115.                      last_character = ';';
  1116.                     }
  1117.  
  1118.                   /*
  1119.                    *  Process each character in the environment entry until
  1120.                    *  an ASCII 0 character is found.
  1121.                    */
  1122.  
  1123.                   while(*path_entry_ptr != (unsigned char)0)
  1124.                     {
  1125.                      /*
  1126.                       *  Check out the next character available for inclusion
  1127.                       *  in the PATH= statement.  If it is invalid, ignore it,
  1128.                       *  otherwise check it further.
  1129.                       */
  1130.  
  1131.                      if(strchr(INVALID_PATH_CHARACTERS,
  1132.                                                  (int)*path_entry_ptr) == NULL)
  1133.                        {
  1134.                         /*
  1135.                          *  Character was not invalid, so now eliminate
  1136.                          *  anything like '\\' or ';;' or '::'.
  1137.                          */
  1138.  
  1139.                         current_character = *path_entry_ptr;
  1140.                         if(!((current_character == last_character) &&
  1141.                               ((current_character == '\\') ||
  1142.                                (current_character == ';') ||
  1143.                                (current_character == ':'))
  1144.                             )
  1145.                           )
  1146.                           {
  1147.                            *new_path_ptr = current_character;
  1148.                            last_character = current_character;
  1149.                            new_path_ptr++;
  1150.                           }
  1151.                        }
  1152.  
  1153.                      /*
  1154.                       *  Regardless of whether or not the character just
  1155.                       *  then considered was valid, move the path entry
  1156.                       *  pointer to the next character to be considered.
  1157.                       */
  1158.  
  1159.                      path_entry_ptr++;
  1160.  
  1161.                     }
  1162.                  }
  1163.  
  1164.                /*
  1165.                 *  Terminate new_path_string with an ASCII 0 character.
  1166.                 */
  1167.  
  1168.                *new_path_ptr = (unsigned char)0;
  1169.  
  1170.                /*
  1171.                 *  The new PATH= string is complete.  Now save the length
  1172.                 *  of the original PATH= entry for later use, free the original
  1173.                 *  entry from memory, and replace it with the new PATH= entry
  1174.                 *  in the environment strings list.
  1175.                 */
  1176.  
  1177.                original_path_length = strlen(environment_strings
  1178.                                                           [path_string_index]);
  1179.  
  1180.                efree((void *)environment_strings[path_string_index]);
  1181.                environment_strings[path_string_index] = new_path_string;
  1182.  
  1183.               }
  1184.  
  1185.             /*
  1186.              *  The modified environment is ready to be written back to
  1187.              *  the original environment area, if necessary.
  1188.              *
  1189.              *  Two possible conditions exist where the environment needs
  1190.              *  to be written back to the original area:
  1191.              *
  1192.              *  (a) PATH= entry and one or more PATHPARTn= entries existed
  1193.              *      in the original environment, and
  1194.              *
  1195.              *  (b) one or more invalid environment entries were found.
  1196.              *
  1197.              *
  1198.              *  Condition (b) is checked as follows:
  1199.              *
  1200.              *  The original number of environment entries, minus the
  1201.              *  number of PATHPARTn= entries in the original environment,
  1202.              *  should equal the number of entries in the modified
  1203.              *  environment.  If, however, when the real number of entries
  1204.              *  in the modified environment is subtracted from the correct
  1205.              *  number of modified environment entries, a non-zero value
  1206.              *  is found, then there were invalid environment entries.
  1207.              *  This last number gives the number of invalid environment
  1208.              *  entries.
  1209.              */
  1210.  
  1211.             invalid_environment_entry_count = environment_string_count -
  1212.                                             (other_environment_index_max + 1) -
  1213.                                             (environment_string_index_max + 1);
  1214.  
  1215.             if(
  1216.                ((path_string_index != -1) &&
  1217.                 (other_environment_index_max != -1)) ||
  1218.                (invalid_environment_entry_count != 0)
  1219.               )
  1220.               {
  1221.                /*
  1222.                 *  Copy the final modified environment strings to the original
  1223.                 *  environment area.  Separate each environment entry with an
  1224.                 *  ASCII 0 character, and terminate the environment area with
  1225.                 *  one extra final ASCII 0 character.
  1226.                 */
  1227.  
  1228.                environment_pointer = saved_environment_pointer;
  1229.  
  1230.                for(i = 0; i <= environment_string_index_max; i++)
  1231.                  {
  1232.                   string_ptr =
  1233.                             environment_strings[environment_string_indexes[i]];
  1234.  
  1235.                   while((*environment_pointer = *string_ptr)
  1236.                                                            != (unsigned char)0)
  1237.                     {
  1238.                      environment_pointer++;
  1239.                      string_ptr++;
  1240.                     }
  1241.  
  1242.                   environment_pointer++;
  1243.                  }
  1244.  
  1245.                *environment_pointer = (unsigned char) 0;
  1246.  
  1247.                /*
  1248.                 *  Now report the environment PATH modification, with
  1249.                 *  segment:offset of environment block.  If the modification
  1250.                 *  was due to a PATH= entry modification, report the original
  1251.                 *  and new PATH sizes.  Also, if the modification was due
  1252.                 *  to invalid environment entries being deleted, report that
  1253.                 *  as well.  Two (c)printf statements are used, because, for
  1254.                 *  some reason, in this version of Turbo C, there's some
  1255.                 *  subtle problem printing, at least using the %d format,
  1256.                 *  after %4.4X is used twice (I think -- the original cprintf
  1257.                 *  statement looked okay to me! :-).
  1258.                 *
  1259.                 *  The value of the reverse_video_flag parameter determines
  1260.                 *  whether (0 value) information is to be given using current
  1261.                 *  text attributes, or (non-zero value) information is to be
  1262.                 *  given using black characters on white background.
  1263.                 */
  1264.  
  1265.  
  1266.                if(reverse_video_flag != 0)
  1267.                  {
  1268.                   textcolor(BLACK); /* Both used in conjunction with cprintf */
  1269.                   textbackground(WHITE);
  1270.                  }
  1271.  
  1272.  
  1273.                if((path_string_index != -1) &&
  1274.                   (other_environment_index_max != -1))
  1275.                  {
  1276.                   cprintf(" COMMAND environment at %4.4X:%4.4X :  ",
  1277.                           ((unsigned long)saved_environment_pointer >> 16),
  1278.                           ((unsigned long)saved_environment_pointer & 0xFFFF));
  1279.                   cprintf("PATH length changed from %d to %d.",
  1280.                                original_path_length,
  1281.                                strlen(environment_strings[path_string_index]));
  1282.  
  1283.                   printf("\n");
  1284.  
  1285.                  }
  1286.  
  1287.                if(invalid_environment_entry_count != 0)
  1288.                  {
  1289.                   cprintf(" COMMAND environment at %4.4X:%4.4X :  ",
  1290.                           ((unsigned long)saved_environment_pointer >> 16),
  1291.                           ((unsigned long)saved_environment_pointer & 0xFFFF));
  1292.  
  1293.                   if(invalid_environment_entry_count == 1)
  1294.                     cprintf("invalid environment entry deleted.");
  1295.                   else cprintf("invalid environment entries deleted.");
  1296.  
  1297.                   printf("\n");
  1298.                  }
  1299.               }
  1300.  
  1301.             /*
  1302.              *  Now free up all memory used by any applicable environment
  1303.              *  string areas and index array areas.
  1304.              */
  1305.  
  1306.             if(environment_strings != (unsigned char **)NULL)
  1307.               {
  1308.                for(i = 0; i < environment_string_count; i++)
  1309.                  efree((void *)environment_strings[i]);
  1310.  
  1311.                efree((void *)environment_strings);
  1312.               }
  1313.  
  1314.             if(environment_string_indexes != (int *)NULL)
  1315.               efree((void *)environment_string_indexes);
  1316.  
  1317.             if(other_environment_indexes != (int *)NULL)
  1318.               efree((void *)other_environment_indexes);
  1319.  
  1320.            }
  1321.  
  1322.  
  1323.         }                             /* End of procedure modify_environment */
  1324.  
  1325. /*****************************************************************************/
  1326.  
  1327.  
  1328. /*
  1329.  *  Procedure abort_utility
  1330.  *
  1331.  *  This procedure, given an exit code, currently calls the builtin  exit
  1332.  *  routine to exit the program.  It is used to simplify localizing all
  1333.  *  utility shutdown routine calls.
  1334.  */
  1335.  
  1336. void abort_utility(error_code)
  1337.  
  1338. int error_code;
  1339.  
  1340. {
  1341.         extern void exit(int);
  1342.  
  1343.         exit(error_code);
  1344.  
  1345. }                                          /* End of procedure abort_utility */
  1346.  
  1347. /*****************************************************************************/
  1348.  
  1349.  
  1350. /*
  1351.  *
  1352.  *  Function emalloc
  1353.  *
  1354.  *  This function augments the builtin  malloc  routine.  Given the amount
  1355.  *  of memory desired for a pointer assignment, it calls  malloc  with that
  1356.  *  value, and checks the pointer value returned.  If the returned pointer
  1357.  *  is NULL, the function displays an error message and shuts down the
  1358.  *  utility by calling the  abort_utility  procedure.  Otherwise, it returns
  1359.  *  the pointer returned by  malloc.
  1360.  */
  1361.  
  1362. void *emalloc(n)
  1363.  
  1364. unsigned n;
  1365.  
  1366. {
  1367.         void *p;
  1368.         void abort_utility(int);
  1369.         /* void *malloc(unsigned); */
  1370.  
  1371.         p = (void *)malloc(n);
  1372.  
  1373.         if (p == NULL)
  1374.           {
  1375.             fprintf(stderr,"\n\nError in malloc -- out of memory.");
  1376.             fprintf(stderr,"\nUtility aborted.\n\n");
  1377.             abort_utility(1);
  1378.           }
  1379.  
  1380.         return(p);
  1381. }                                                 /* End of function emalloc */
  1382.  
  1383. /*****************************************************************************/
  1384.  
  1385.  
  1386. /*
  1387.  *
  1388.  *  Function erealloc
  1389.  *
  1390.  *  This function augments the builtin  realloc  routine.  Given a pointer,
  1391.  *  along with the new amount of memory, which, after being allocated, the
  1392.  *  pointer's referenced area is to be copied to, it calls  realloc  with the
  1393.  *  pointer and new memory area size.  If the pointer returned by  realloc
  1394.  *  is NULL, the function displays an error message and shuts down the utility
  1395.  *  by calling the  abort_utility  procedure.  Otherwise, it returns the
  1396.  *  pointer returned by  realloc.
  1397.  *
  1398.  */
  1399.  
  1400. void *erealloc (ptr, newsize)
  1401.  
  1402. void     *ptr;
  1403. unsigned newsize;
  1404.  
  1405. {
  1406.         void *p;
  1407.         void abort_utility(int);
  1408.         /* void *realloc(void *, unsigned); */
  1409.  
  1410.         p = (void *)realloc(ptr, newsize);
  1411.  
  1412.         if (p == NULL)
  1413.           {
  1414.             fprintf(stderr,"\n\nError in realloc -- out of memory.");
  1415.             fprintf(stderr,"\nUtility aborted.\n\n");
  1416.             abort_utility(1);
  1417.           }
  1418.  
  1419.         return (p);
  1420.  
  1421. }                                                /* End of function erealloc */
  1422.  
  1423. /*****************************************************************************/
  1424.  
  1425.  
  1426. /*
  1427.  *
  1428.  *  Procedure efree
  1429.  *
  1430.  *  This procedure, given a pointer, calls the builtin  free  routine to
  1431.  *  free-up the memory pointed to by that pointer.
  1432.  *
  1433.  */
  1434.  
  1435. void efree(p)
  1436.  
  1437. void *p;
  1438.  
  1439. {
  1440.         /* extern void free(void *); */
  1441.  
  1442.         free(p);
  1443.  
  1444. }                                                  /* End of procedure efree */
  1445.  
  1446. /*****************************************************************************/
  1447.  
  1448.  
  1449. /*
  1450.  *  Procedure usage
  1451.  *
  1452.  *  This procedure gives the user a description of what the XTNDPATH utility
  1453.  *  does and how to use it.
  1454.  */
  1455.  
  1456. void usage(void)
  1457.  
  1458.         {
  1459.          printf(
  1460.  
  1461. "XTNDPATH - (eXTeND PATH):  XTNDPATH [-REV] [-PREFIX Alternate-Prefix]\n"
  1462. "           (bracketed items are optional; don't include [] when specifying)\n"
  1463. "         Extends DOS path command by appending all DOS environment entries\n"
  1464. "         that start with  PATHPART  to the  PATH  entry.  XTNDPATH can\n"
  1465. "         modify all COMMAND.COM environment areas it finds in memory, even\n"
  1466. "         deleting invalid (no '=' character) environment entries.\n"
  1467. "\n"
  1468. "         Set up your path in the usual way, and specify the remaining paths\n"
  1469. "         (to be included) in one or more environment entries, each\n"
  1470. "         beginning with PATHPART (the default Prefix).  All appended\n"
  1471. "         path-part entries are deleted from the final modified environment.\n"
  1472. "         For example, enter these lines at the COMMAND prompt:\n"
  1473. "             SET PATHPARTqwerty=C:\\UTILITY\n"
  1474. "             PATH=C:\\;C:\\DOS;\n"
  1475. "             SET PATHPART = C:\\TEMP\n"
  1476. "             XTNDPATH\n"
  1477. "             SET\n"
  1478. "\n"
  1479. "         One line of the SET command output should look like the following:\n"
  1480. "\n"
  1481. "             PATH=C:\\;C:\\DOS;C:\\UTILITY;C:\\TEMP\n"
  1482. "\n"
  1483. "         Written by David Douglas : douglas@usceast.cs.scarolina.edu\n");
  1484.  
  1485.         }                                          /* End of procedure usage */
  1486.  
  1487. /*****************************************************************************/
  1488. /******************** End of XTNDPATH utility source code ********************/
  1489. /*****************************************************************************/
  1490.